#!/usr/bin/env pypy

import sys
import re
import itertools
from itertools import izip_longest
from itertools import imap
from collections import Counter
import argparse
import datetime
import time

def parse_range(astr):
    result = set()
    for part in astr.split(','):
        x = part.split('-')
        result.update(range(int(x[0]), int(x[-1]) + 1))
    return sorted(result)

def sortby(x):
   # return x.split()[0]
   # largest, address, order
   return int(x.split()[2]), x.split()[0], int(x.split()[3])

def sortby1(x):
   # return x.split()[0]
   # largest, address, order
   return int(x.split()[1]), x.split()[0]


def lists_overlap(a, b):
  sb = set(b)
  return any(itertools.imap(sb.__contains__, a))

cyclesdata = []
cyclesresultstrings = []
cyclesuses = []


def main():

   global cyclesdata

   cycledata = []

   if len(sys.argv) >= 6:

      cyclesrange = parse_range(sys.argv[4])

      cyclenr = 0
      allow = False

      order = 1

      f = open(sys.argv[1])

      sys.stderr.write('collecting data for cycles ')

      for line in f:

         if line.startswith('DSC'):

            if re.search(sys.argv[2], line):

               if cycledata:

                  sys.stderr.write('[%d]' %int(cyclenr))
                  sys.stderr.flush()

                  addrs = []
                  largests = []
                  for cd in cycledata:
                     cdl = list(cd)
                     a = cdl[0]
                     s = cdl[2]
                     if a not in addrs:
                        addrs.append(a)
                        largests.append(s)
                     else:
                        if s > largests[addrs.index(a)]:
                           largests[addrs.index(a)] = s

                  i = 0;
                  for c in cycledata:
                     b = largests[addrs.index(list(c)[0])]
                     cycledata[i] = cycledata[i] + (b,)
                     i+= 1

                  cyclesdata.append(cycledata)
                  cyclesresultstrings.append([''] * len(cycledata))
                  cyclesuses.append([0] * len(cycledata))


               cycledata = []
               cyclenr += 1

               order = 1

               allow = True

               if cyclenr not in cyclesrange:
                  if cyclenr > max(cyclesrange):
                     break
                  else:
                     sys.stderr.write('-')
                     sys.stderr.flush()


            elif re.search(sys.argv[3], line):

               allow = False


         if allow:

            if cyclenr in cyclesrange:

               if line.startswith('DSC'):

                  #sys.stderr.write('%s\n' %line);

                  result = re.search(r'(m|c|r|ma|n|n\[\]),@0=[0-9a-f]+@1=[0-9a-f]+@2=[0-9a-f]+', line)

                  if result:

                     malloc = int((result.group().split('@2=')[1]), 16)
                     size = int((result.group().split('@1=')[1].split('@2=')[0]), 16)

                     tag = line.split(' ')[3][:-1].split(',=')[0]
                     tag = tag.replace(',', ';')

                     #sys.stderr.write('tag=[%s],malloc=%x,size=%x\n' %(tag, malloc, size));

                     if malloc < 0x30000000:

                        cycledata.append((malloc, tag, size, order))
                        order+= 1

      sys.stderr.write(' (%d cycles)\n' %len(cyclesdata))

      f.close()

      leakchecklists = []

      nrcyclestocheck = max(cyclesrange) - min(cyclesrange) + 1

      if nrcyclestocheck > len(cyclesdata):
         nrcyclestocheck = len(cyclesdata)

      sys.stderr.write('checking %d cycle(s)\n' %nrcyclestocheck)

      nrchecks = 1
      ts = datetime.datetime.now()

      for cycletocheckagainst in range(0, nrcyclestocheck):

         cycle = cyclesdata[cycletocheckagainst]
         cyclerange = []
         for item in cycle:
            cyclerange.extend(range(item[0], item[0] + item[2]))
         cycleset = set(cyclerange)

         #check this one against all cycles
         for cycletocheck in range(0, nrcyclestocheck):

            if cycletocheckagainst > cycletocheck:

               tf = datetime.datetime.now()

               if len(sys.argv) >= 6:
                  if (cycletocheck + 1) > int(sys.argv[5]):
                     continue

               if len(sys.argv) == 7:
                  if (cycletocheckagainst - cycletocheck) >= int(sys.argv[6]):
                     continue;

               sys.stderr.write('(%s)#%-5d checking %d-%d\n' % (str(tf-ts), nrchecks, cycletocheck + 1, cycletocheckagainst + 1))
               cycleresultstrings = cyclesresultstrings[cycletocheck]
               cycleuses = cyclesuses[cycletocheck]

               cycledata = cyclesdata[cycletocheck]

               # do the check
               i = 0
               for item in cycledata:

                  if not cycleresultstrings[i]:
                     cycleresultstrings[i] = str('0x%08x %6d %6d %6d %s=x') %(item[0], item[2], item[4], item[3], item[1])

                  rs = set(range(item[0], item[0] + item[2]))
                  result = bool(rs & cycleset)
                  if result:
                     cycleresultstrings[i]+= '+'
                     cycleuses[i]+= 1
                  else:
                     cycleresultstrings[i]+= '-'

                  i+= 1

               cyclesresultstrings[cycletocheck] = cycleresultstrings
               cyclesuses[cycletocheck] = cycleuses
               nrchecks+= 1


               if len(sys.argv) == 7:
                  if (cycletocheckagainst + 1 - cycletocheck == int(sys.argv[6])):
                     sys.stderr.write('just checked %d-%d\n' % (cycletocheck + 1, cycletocheckagainst + 1))

                     #add result
                     leakchecklist_full = []
                     leakchecklist_leaksonly = []
                     leakchecklist_leaksonly_minimized = []

                     cycleresultstrings = cyclesresultstrings[cycletocheck]
                     cycleuses = cyclesuses[cycletocheck]

                     k = 0
                     for t in cycleresultstrings:
                        if cycleuses[k] == 0:
                           cycleresultstrings[k]+= '!'
                           leakchecklist_leaksonly.append(cycleresultstrings[k].split('x=')[0])
                           #sys.stderr.write('%s\n' % cycleresultstrings[k])
                        k+= 1
                 
                     leakchecklist_leaksonly.sort(key = sortby, reverse=True)

                     addrs = []
                     total = 0
                     for r in leakchecklist_leaksonly:
                        if not r.split()[0] in addrs:
                           leakchecklist_leaksonly_minimized.append(str('%s %s' %((r.split()[4]).split('=x')[0], r.split()[1])))
                           addrs.append(r.split()[0])
                           total += int(r.split()[1])

                     leakchecklist_leaksonly_minimized.sort(key = sortby1, reverse=True)
                     for r in leakchecklist_leaksonly_minimized:
                        sys.stderr.write('cycle(%-3d)[%s]\n' % (cycletocheck + 1, str(r)))
                     sys.stderr.write('cycle(%-3d)total = %d Bytes\n' % (cycletocheck + 1, total))
                     leakchecklist_leaksonly_minimized.insert(0, 'total = %d Bytes' %total)

                     leakchecklists.append(leakchecklist_leaksonly_minimized)

      for row in izip_longest(*leakchecklists):
         print row

   else:
      sys.stderr.write("Usage: %s tdifile cyclepattern-start cyclepattern-end cyclerange [maxnrcyclestocheck] [maxnrfuturecyclestocheckagainst]\n" % sys.argv[0])

if __name__ == "__main__":
   main()
